#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Blender脚本：将FrankMocap的npz动画数据转换为FBX
适用于Blender 4.5+

使用方法：
1. 在Blender中打开脚本编辑器
2. 加载此脚本并运行
3. 在弹出的文件浏览器中选择npz文件
4. 脚本将创建SMPL-X骨架和动画，并导出FBX

作者：AI Assistant
"""

import bpy
import bmesh
import numpy as np
from mathutils import Vector, Euler, Matrix
import os
from bpy_extras.io_utils import ImportHelper
from bpy.props import StringProperty
from bpy.types import Operator

# SMPL-X骨骼层次结构（24个身体关节）
SMPLX_SKELETON = {
    'pelvis': {'parent': None, 'index': 0},
    'left_hip': {'parent': 'pelvis', 'index': 1},
    'right_hip': {'parent': 'pelvis', 'index': 2},
    'spine1': {'parent': 'pelvis', 'index': 3},
    'left_knee': {'parent': 'left_hip', 'index': 4},
    'right_knee': {'parent': 'right_hip', 'index': 5},
    'spine2': {'parent': 'spine1', 'index': 6},
    'left_ankle': {'parent': 'left_knee', 'index': 7},
    'right_ankle': {'parent': 'right_knee', 'index': 8},
    'spine3': {'parent': 'spine2', 'index': 9},
    'left_foot': {'parent': 'left_ankle', 'index': 10},
    'right_foot': {'parent': 'right_ankle', 'index': 11},
    'neck': {'parent': 'spine3', 'index': 12},
    'left_collar': {'parent': 'spine3', 'index': 13},
    'right_collar': {'parent': 'spine3', 'index': 14},
    'head': {'parent': 'neck', 'index': 15},
    'left_shoulder': {'parent': 'left_collar', 'index': 16},
    'right_shoulder': {'parent': 'right_collar', 'index': 17},
    'left_elbow': {'parent': 'left_shoulder', 'index': 18},
    'right_elbow': {'parent': 'right_shoulder', 'index': 19},
    'left_wrist': {'parent': 'left_elbow', 'index': 20},
    'right_wrist': {'parent': 'right_elbow', 'index': 21},
    'left_hand': {'parent': 'left_wrist', 'index': 22},
    'right_hand': {'parent': 'right_wrist', 'index': 23}
}

# 手部骨骼定义（每只手21个关节）
HAND_BONES = [
    'wrist', 'thumb1', 'thumb2', 'thumb3', 'thumb_tip',
    'index1', 'index2', 'index3', 'index_tip',
    'middle1', 'middle2', 'middle3', 'middle_tip',
    'ring1', 'ring2', 'ring3', 'ring_tip',
    'pinky1', 'pinky2', 'pinky3', 'pinky_tip'
]

def clear_scene():
    """清理场景"""
    # 删除所有网格、骨架和灯光
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete(use_global=False)
    
    # 清理孤立数据
    for block in bpy.data.meshes:
        if block.users == 0:
            bpy.data.meshes.remove(block)
    
    for block in bpy.data.armatures:
        if block.users == 0:
            bpy.data.armatures.remove(block)

def create_smplx_armature():
    """创建SMPL-X骨架"""
    # 创建骨架
    bpy.ops.object.armature_add(enter_editmode=True, location=(0, 0, 0))
    armature_obj = bpy.context.active_object
    armature_obj.name = "SMPLX_Armature"
    armature = armature_obj.data
    armature.name = "SMPLX_Armature"
    
    # 进入编辑模式创建骨骼
    bpy.context.view_layer.objects.active = armature_obj
    bpy.ops.object.mode_set(mode='EDIT')
    
    # 删除默认骨骼
    bpy.ops.armature.select_all(action='SELECT')
    bpy.ops.armature.delete()
    
    # SMPL-X标准骨骼位置（基于T-pose）
    smplx_bone_positions = {
        'pelvis': ((0, 0, 0.9), (0, 0, 1.0)),
        'left_hip': ((-0.1, 0, 0.85), (-0.1, 0, 0.5)),
        'right_hip': ((0.1, 0, 0.85), (0.1, 0, 0.5)),
        'spine1': ((0, 0, 1.0), (0, 0, 1.2)),
        'left_knee': ((-0.1, 0, 0.5), (-0.1, 0, 0.15)),
        'right_knee': ((0.1, 0, 0.5), (0.1, 0, 0.15)),
        'spine2': ((0, 0, 1.2), (0, 0, 1.4)),
        'left_ankle': ((-0.1, 0, 0.15), (-0.1, 0, 0.05)),
        'right_ankle': ((0.1, 0, 0.15), (0.1, 0, 0.05)),
        'spine3': ((0, 0, 1.4), (0, 0, 1.6)),
        'left_foot': ((-0.1, 0, 0.05), (-0.1, 0.1, 0.05)),
        'right_foot': ((0.1, 0, 0.05), (0.1, 0.1, 0.05)),
        'neck': ((0, 0, 1.6), (0, 0, 1.7)),
        'left_collar': ((-0.05, 0, 1.5), (-0.15, 0, 1.5)),
        'right_collar': ((0.05, 0, 1.5), (0.15, 0, 1.5)),
        'head': ((0, 0, 1.7), (0, 0, 1.85)),
        'left_shoulder': ((-0.15, 0, 1.5), (-0.4, 0, 1.45)),
        'right_shoulder': ((0.15, 0, 1.5), (0.4, 0, 1.45)),
        'left_elbow': ((-0.4, 0, 1.45), (-0.65, 0, 1.4)),
        'right_elbow': ((0.4, 0, 1.45), (0.65, 0, 1.4)),
        'left_wrist': ((-0.65, 0, 1.4), (-0.8, 0, 1.35)),
        'right_wrist': ((0.65, 0, 1.4), (0.8, 0, 1.35)),
        'left_hand': ((-0.8, 0, 1.35), (-0.9, 0, 1.35)),
        'right_hand': ((0.8, 0, 1.35), (0.9, 0, 1.35))
    }
    
    # 创建身体骨骼
    bones = {}
    for bone_name, bone_info in SMPLX_SKELETON.items():
        bone = armature.edit_bones.new(bone_name)
        if bone_name in smplx_bone_positions:
            head_pos, tail_pos = smplx_bone_positions[bone_name]
            bone.head = head_pos
            bone.tail = tail_pos
        else:
            # 备用位置
            bone.head = (0, 0, bone_info['index'] * 0.1)
            bone.tail = (0, 0, bone_info['index'] * 0.1 + 0.1)
        bones[bone_name] = bone
    
    # 设置父子关系
    for bone_name, bone_info in SMPLX_SKELETON.items():
        if bone_info['parent'] and bone_info['parent'] in bones:
            bones[bone_name].parent = bones[bone_info['parent']]
    
    # 简化手部骨骼（只创建主要关节）
    for side in ['left', 'right']:
        if f'{side}_hand' in bones:
            hand_parent = bones[f'{side}_hand']
            sign = -1 if side == 'left' else 1
            
            # 只创建5个主要手指骨骼
            for i in range(5):
                bone_name = f'{side}_finger_{i}'
                bone = armature.edit_bones.new(bone_name)
                bone.head = (sign * (0.8 + i * 0.02), 0, 1.35)
                bone.tail = (sign * (0.8 + i * 0.02), 0, 1.3)
                bone.parent = hand_parent
    
    bpy.ops.object.mode_set(mode='OBJECT')
    return armature_obj

def create_smplx_mesh(vertices, faces):
    """创建SMPL-X网格"""
    # 创建网格
    mesh = bpy.data.meshes.new("SMPLX_Mesh")
    obj = bpy.data.objects.new("SMPLX_Character", mesh)
    bpy.context.collection.objects.link(obj)
    
    # 使用第一帧的顶点数据
    frame_0_vertices = vertices[0] if len(vertices.shape) > 2 else vertices
    
    # 创建网格数据
    mesh.from_pydata(frame_0_vertices.tolist(), [], faces.tolist())
    mesh.update()
    
    return obj

def create_vertex_groups(mesh_obj, armature_obj):
    """为网格创建顶点组（对应骨骼）"""
    # 为每个骨骼创建顶点组
    for bone_name in SMPLX_SKELETON.keys():
        if bone_name not in mesh_obj.vertex_groups:
            mesh_obj.vertex_groups.new(name=bone_name)
    
    # 手部骨骼顶点组
    for side in ['left', 'right']:
        for finger_bone in HAND_BONES[1:]:  # 跳过wrist
            bone_name = f'{side}_hand_{finger_bone}'
            if bone_name not in mesh_obj.vertex_groups:
                mesh_obj.vertex_groups.new(name=bone_name)

def apply_automatic_weights(mesh_obj, armature_obj):
    """应用自动权重绑定"""
    # 确保网格是激活对象
    bpy.context.view_layer.objects.active = mesh_obj
    mesh_obj.select_set(True)
    armature_obj.select_set(True)
    
    # 创建顶点组
    create_vertex_groups(mesh_obj, armature_obj)
    
    # 使用自动权重
    bpy.context.view_layer.objects.active = armature_obj
    bpy.ops.object.parent_set(type='ARMATURE_AUTO')
    
    # 如果自动权重失败，使用简单的距离权重
    if not mesh_obj.vertex_groups:
        print("Auto weights failed, applying distance-based weights...")
        apply_distance_weights(mesh_obj, armature_obj)

def apply_distance_weights(mesh_obj, armature_obj):
    """基于距离的权重绑定（备用方法）"""
    import bmesh
    
    # 进入编辑模式
    bpy.context.view_layer.objects.active = mesh_obj
    bpy.ops.object.mode_set(mode='EDIT')
    
    # 创建bmesh
    bm = bmesh.from_mesh(mesh_obj.data)
    
    # 确保有顶点组
    create_vertex_groups(mesh_obj, armature_obj)
    
    # 为每个顶点分配权重（简单方法：所有顶点给pelvis权重1.0）
    pelvis_group = mesh_obj.vertex_groups.get('pelvis')
    if pelvis_group:
        # 选择所有顶点
        bpy.ops.mesh.select_all(action='SELECT')
        # 分配权重
        bpy.ops.object.vertex_group_set_active(group='pelvis')
        bpy.ops.object.vertex_group_assign(weight=1.0)
    
    # 更新网格
    bmesh.update_edit_mesh(mesh_obj.data)
    bpy.ops.object.mode_set(mode='OBJECT')

def apply_smplx_weights(mesh_obj, armature_obj):
    """应用SMPL-X特定的权重（基于顶点位置的智能分配）"""
    num_vertices = len(mesh_obj.data.vertices)
    vertices = mesh_obj.data.vertices
    
    # 创建顶点组
    create_vertex_groups(mesh_obj, armature_obj)
    
    print(f"Applying intelligent weights to {num_vertices} vertices...")
    
    # 基于顶点Z坐标的智能分配
    pelvis_group = mesh_obj.vertex_groups.get('pelvis')
    spine3_group = mesh_obj.vertex_groups.get('spine3')
    head_group = mesh_obj.vertex_groups.get('head')
    left_shoulder_group = mesh_obj.vertex_groups.get('left_shoulder')
    right_shoulder_group = mesh_obj.vertex_groups.get('right_shoulder')
    left_hip_group = mesh_obj.vertex_groups.get('left_hip')
    right_hip_group = mesh_obj.vertex_groups.get('right_hip')
    
    for i, vertex in enumerate(vertices):
        x, y, z = vertex.co
        
        # 基于高度分配主要权重
        if z > 1.6:  # 头部和颈部
            if head_group:
                head_group.add([i], 1.0, 'REPLACE')
        elif z > 1.2:  # 上躯干和手臂
            if x < -0.3:  # 左臂
                if left_shoulder_group:
                    left_shoulder_group.add([i], 1.0, 'REPLACE')
            elif x > 0.3:  # 右臂
                if right_shoulder_group:
                    right_shoulder_group.add([i], 1.0, 'REPLACE')
            else:  # 躯干
                if spine3_group:
                    spine3_group.add([i], 1.0, 'REPLACE')
        elif z > 0.4:  # 下躯干和大腿
            if x < -0.05:  # 左侧
                if left_hip_group:
                    left_hip_group.add([i], 1.0, 'REPLACE')
            elif x > 0.05:  # 右侧
                if right_hip_group:
                    right_hip_group.add([i], 1.0, 'REPLACE')
            else:  # 中央
                if pelvis_group:
                    pelvis_group.add([i], 1.0, 'REPLACE')
        else:  # 小腿和脚部
            if x < 0:  # 左腿
                if left_hip_group:
                    left_hip_group.add([i], 0.8, 'REPLACE')
            else:  # 右腿
                if right_hip_group:
                    right_hip_group.add([i], 0.8, 'REPLACE')
    
    print(f"Applied intelligent weights to {num_vertices} vertices")

def apply_animation_data(armature_obj, animation_data):
    """应用动画数据到骨架"""
    num_frames = animation_data['num_frames']
    fps = animation_data.get('fps', 30.0)
    
    print(f"Applying animation: {num_frames} frames at {fps} fps")
    
    # 设置场景
    scene = bpy.context.scene
    scene.frame_start = 1
    scene.frame_end = num_frames
    scene.render.fps = int(fps)
    
    # 获取骨架数据
    armature = armature_obj.data
    
    # 进入姿态模式
    bpy.context.view_layer.objects.active = armature_obj
    bpy.ops.object.mode_set(mode='POSE')
    
    # 清除现有动画数据
    if armature_obj.animation_data:
        armature_obj.animation_data_clear()
    
    # 创建动作
    action = bpy.data.actions.new(name="SMPLX_Animation")
    armature_obj.animation_data_create()
    armature_obj.animation_data.action = action
    
    # 重置所有骨骼到默认姿态
    for pose_bone in armature_obj.pose.bones:
        pose_bone.rotation_mode = 'XYZ'
        pose_bone.rotation_euler = (0, 0, 0)
        pose_bone.location = (0, 0, 0)
        pose_bone.scale = (1, 1, 1)
    
    # 应用身体姿态数据
    if 'body_pose' in animation_data:
        body_poses = animation_data['body_pose']
        print(f"Applying body poses: {body_poses.shape}")
        
        # 检查数据维度
        if len(body_poses.shape) == 3:
            body_poses = body_poses.reshape(num_frames, -1)
        
        applied_frames = 0
        for frame in range(num_frames):
            scene.frame_set(frame + 1)
            
            # 应用身体姿态（旋转）
            pose_data = body_poses[frame].reshape(-1, 3)  # 重塑为(24, 3)或更多
            
            bones_animated = 0
            for bone_name, bone_info in SMPLX_SKELETON.items():
                if bone_name in armature_obj.pose.bones:
                    pose_bone = armature_obj.pose.bones[bone_name]
                    bone_index = bone_info['index']
                    
                    if bone_index < len(pose_data):
                        # 转换轴角旋转到欧拉角
                        axis_angle = pose_data[bone_index]
                        angle = np.linalg.norm(axis_angle)
                        
                        if angle > 0.001:  # 添加最小阈值
                            axis = axis_angle / angle
                            # 创建旋转矩阵
                            rot_matrix = Matrix.Rotation(angle, 4, Vector(axis))
                            # 转换为欧拉角
                            euler = rot_matrix.to_euler()
                            pose_bone.rotation_euler = euler
                            bones_animated += 1
                        else:
                            pose_bone.rotation_euler = (0, 0, 0)
                        
                        # 插入关键帧
                        pose_bone.keyframe_insert(data_path="rotation_euler", frame=frame + 1)
            
            if bones_animated > 0:
                applied_frames += 1
        
        print(f"Applied animation to {applied_frames} frames with bone rotations")
    
    # 应用根节点平移（如果有的话）
    if 'transl' in animation_data:
        transl_data = animation_data['transl']
        print(f"Applying translation: {transl_data.shape}")
        
        if len(transl_data.shape) >= 2:
            for frame in range(num_frames):
                scene.frame_set(frame + 1)
                translation = transl_data[frame].flatten()
                if len(translation) >= 3:
                    armature_obj.location = Vector((translation[0], translation[1], translation[2]))
                    armature_obj.keyframe_insert(data_path="location", frame=frame + 1)
            print(f"Applied root translation to {num_frames} frames")
    
    # 应用手部动画（如果有的话）
    for side in ['left', 'right']:
        hand_key = f'{side}_hand_pose'
        if hand_key in animation_data:
            hand_poses = animation_data[hand_key]
            print(f"Applying {side} hand poses: {hand_poses.shape}")
            
            if len(hand_poses.shape) >= 2:
                for frame in range(num_frames):
                    scene.frame_set(frame + 1)
                    hand_pose_data = hand_poses[frame].reshape(-1, 3)
                    
                    # 应用到手部骨骼（限制到可用的骨骼数量）
                    max_finger_bones = min(len(HAND_BONES[1:6]), len(hand_pose_data))
                    for i in range(max_finger_bones):
                        finger_bone = HAND_BONES[1 + i]
                        bone_name = f'{side}_hand_{finger_bone}'
                        if bone_name in armature_obj.pose.bones:
                            pose_bone = armature_obj.pose.bones[bone_name]
                            axis_angle = hand_pose_data[i]
                            angle = np.linalg.norm(axis_angle)
                            
                            if angle > 0.001:
                                axis = axis_angle / angle
                                rot_matrix = Matrix.Rotation(angle, 4, Vector(axis))
                                euler = rot_matrix.to_euler()
                                pose_bone.rotation_euler = euler
                            else:
                                pose_bone.rotation_euler = (0, 0, 0)
                            
                            pose_bone.keyframe_insert(data_path="rotation_euler", frame=frame + 1)
    
    # 添加简单的测试动画（如果没有检测到明显的动画变化）
    if applied_frames < 10:
        print("Warning: Very few animated frames detected, adding test animation...")
        add_test_animation(armature_obj, num_frames)
    
    bpy.ops.object.mode_set(mode='OBJECT')
    print(f"Animation applied: {num_frames} frames at {fps} fps")

def add_test_animation(armature_obj, num_frames):
    """添加简单的测试动画以验证系统工作"""
    if 'head' in armature_obj.pose.bones:
        head_bone = armature_obj.pose.bones['head']
        for frame in range(num_frames):
            bpy.context.scene.frame_set(frame + 1)
            # 简单的头部左右摆动
            rotation_y = 0.2 * np.sin(frame * 0.1)  # 左右摆动
            head_bone.rotation_euler = (0, rotation_y, 0)
            head_bone.keyframe_insert(data_path="rotation_euler", frame=frame + 1)
        print("Added test head animation")

def validate_animation_data(data):
    """验证和分析动画数据"""
    print("\n=== Animation Data Validation ===")
    
    # 检查基本信息
    num_frames = data.get('num_frames', 0)
    print(f"Frames: {num_frames}")
    print(f"FPS: {data.get('fps', 30)}")
    print(f"Duration: {data.get('total_duration', 0):.2f} seconds")
    
    # 检查姿态数据
    if 'body_pose' in data:
        body_pose = data['body_pose']
        print(f"Body pose shape: {body_pose.shape}")
        
        # 检查动画变化幅度
        if len(body_pose.shape) >= 2 and body_pose.shape[0] > 1:
            pose_diff = np.diff(body_pose.reshape(body_pose.shape[0], -1), axis=0)
            max_change = np.max(np.abs(pose_diff))
            avg_change = np.mean(np.abs(pose_diff))
            print(f"Max pose change per frame: {max_change:.6f}")
            print(f"Average pose change per frame: {avg_change:.6f}")
            
            if max_change < 0.001:
                print("⚠️  WARNING: Very small pose changes detected - animation may appear static")
        else:
            print("⚠️  WARNING: Insufficient pose data for analysis")
    else:
        print("❌ No body_pose data found")
    
    # 检查平移数据
    if 'transl' in data:
        transl = data['transl']
        print(f"Translation shape: {transl.shape}")
        if len(transl.shape) >= 2 and transl.shape[0] > 1:
            transl_diff = np.diff(transl.reshape(transl.shape[0], -1), axis=0)
            max_transl_change = np.max(np.abs(transl_diff))
            print(f"Max translation change per frame: {max_transl_change:.6f}")
    else:
        print("ℹ️  No translation data found")
    
    # 检查手部数据
    for side in ['left', 'right']:
        hand_key = f'{side}_hand_pose'
        if hand_key in data:
            hand_pose = data[hand_key]
            print(f"{side.capitalize()} hand pose shape: {hand_pose.shape}")
        else:
            print(f"ℹ️  No {side} hand pose data found")
    
    print("=== End Validation ===\n")

def apply_vertex_animation_only(mesh_obj, vertices_data):
    """使用纯顶点动画（形状键）来实现动画"""
    if len(vertices_data.shape) < 3:
        print("No vertex animation data found")
        return
    
    num_frames = vertices_data.shape[0]
    print(f"Creating vertex animation with {num_frames} frames...")
    
    # 设置场景动画范围
    scene = bpy.context.scene
    scene.frame_start = 1
    scene.frame_end = num_frames
    
    # 确保网格是活动对象
    bpy.context.view_layer.objects.active = mesh_obj
    mesh_obj.select_set(True)
    
    # 添加基础形状键
    if not mesh_obj.data.shape_keys:
        mesh_obj.shape_key_add(name="Basis")
    
    # 每10帧创建一个关键形状键（减少数据量）
    key_frame_interval = max(1, num_frames // 50)  # 最多50个形状键
    
    shape_keys = []
    for frame in range(0, num_frames, key_frame_interval):
        shape_key_name = f"Frame_{frame:04d}"
        shape_key = mesh_obj.shape_key_add(name=shape_key_name)
        
        # 设置顶点位置
        frame_vertices = vertices_data[frame]
        for i, vertex_data in enumerate(frame_vertices):
            if i < len(shape_key.data):
                shape_key.data[i].co = vertex_data
        
        shape_keys.append((frame, shape_key))
        print(f"Created shape key for frame {frame}")
    
    # 为形状键设置动画
    for i, (frame, shape_key) in enumerate(shape_keys):
        # 所有形状键在开始时权重为0
        shape_key.value = 0.0
        shape_key.keyframe_insert(data_path="value", frame=1)
        
        # 在对应帧设置权重为1
        shape_key.value = 1.0
        shape_key.keyframe_insert(data_path="value", frame=frame + 1)
        
        # 在下一个关键帧之前设置权重为0
        next_frame = shape_keys[i + 1][0] if i + 1 < len(shape_keys) else num_frames
        shape_key.value = 0.0
        shape_key.keyframe_insert(data_path="value", frame=next_frame)
        
        # 在动画结束时权重为0
        shape_key.value = 0.0
        shape_key.keyframe_insert(data_path="value", frame=num_frames)
    
    print(f"✅ Vertex animation applied with {len(shape_keys)} shape keys")

def apply_mesh_animation(mesh_obj, vertices_data):
    """应用网格顶点动画"""
    if len(vertices_data.shape) < 3:
        print("No vertex animation data found")
        return
    
    num_frames = vertices_data.shape[0]
    mesh = mesh_obj.data
    
    # 创建形状键
    if not mesh.shape_keys:
        # 添加基础形状键
        mesh_obj.shape_key_add(name="Basis")
    
    # 为每一帧创建形状键
    scene = bpy.context.scene
    
    for frame in range(min(num_frames, 100)):  # 限制形状键数量
        shape_key = mesh_obj.shape_key_add(name=f"Frame_{frame}")
        
        # 设置顶点位置
        frame_vertices = vertices_data[frame]
        for i, vertex in enumerate(shape_key.data):
            if i < len(frame_vertices):
                vertex.co = Vector(frame_vertices[i])
        
        # 设置关键帧
        shape_key.value = 0.0
        shape_key.keyframe_insert(data_path="value", frame=1)
        
        shape_key.value = 1.0 if frame == 0 else 0.0
        shape_key.keyframe_insert(data_path="value", frame=frame + 1)
        
        shape_key.value = 0.0
        shape_key.keyframe_insert(data_path="value", frame=num_frames)

class NPZToFBXOperator(bpy.types.Operator, ImportHelper):
    """从NPZ文件创建SMPL-X角色和动画"""
    bl_idname = "import_anim.npz_to_fbx"
    bl_label = "Import NPZ Animation"
    bl_description = "从FrankMocap NPZ文件导入SMPL-X动画"
    
    filename_ext = ".npz"
    filter_glob: StringProperty(
        default="*.npz",
        options={'HIDDEN'},
        maxlen=255,
    )
    
    def execute(self, context):
        return self.import_npz_animation(context, self.filepath)
    
    def import_npz_animation(self, context, filepath):
        try:
            print(f"Loading NPZ file: {filepath}")
            
            # 加载NPZ数据
            data = np.load(filepath)
            print(f"NPZ keys: {list(data.keys())}")
            
            # 清理场景
            clear_scene()
            
            # 创建骨架
            print("Creating SMPL-X armature...")
            armature_obj = create_smplx_armature()
            
            # 创建网格（如果有顶点数据）
            mesh_obj = None
            if 'vertices' in data and 'faces' in data:
                print("Creating SMPL-X mesh...")
                vertices = data['vertices']
                faces = data['faces']
                mesh_obj = create_smplx_mesh(vertices, faces)
                
                # 应用骨骼权重绑定
                print("Applying bone weights...")
                try:
                    # 尝试使用SMPL-X特定的权重
                    apply_smplx_weights(mesh_obj, armature_obj)
                    
                    # 设置父子关系
                    mesh_obj.select_set(True)
                    armature_obj.select_set(True)
                    context.view_layer.objects.active = armature_obj
                    bpy.ops.object.parent_set(type='ARMATURE')
                    
                    print("✅ Bone weights applied successfully")
                    
                except Exception as e:
                    print(f"Warning: Weight binding error: {e}")
                    print("Trying automatic weights...")
                    # 备用方法：自动权重
                    apply_automatic_weights(mesh_obj, armature_obj)
            
            # 检查和验证动画数据
            print("Validating animation data...")
            validate_animation_data(data)
            
            # 应用动画数据
            print("Applying animation data...")
            apply_animation_data(armature_obj, data)
            
            # 应用网格动画（如果需要）
            if mesh_obj and 'vertices' in data:
                print("Applying mesh animation...")
                apply_mesh_animation(mesh_obj, data['vertices'])
            
            # 设置视图
            bpy.ops.view3d.view_all()
            
            # 导出FBX
            output_path = filepath.replace('.npz', '.fbx')
            print(f"Exporting to FBX: {output_path}")
            
            # 选择所有对象
            bpy.ops.object.select_all(action='SELECT')
            
            # 导出FBX
            bpy.ops.export_scene.fbx(
                filepath=output_path,
                check_existing=True,
                use_selection=True,
                # 动画设置
                bake_anim=True,
                bake_anim_use_all_bones=True,
                bake_anim_use_nla_strips=False,
                bake_anim_use_all_actions=False,
                bake_anim_force_startend_keying=True,
                # 骨骼设置
                add_leaf_bones=False,
                primary_bone_axis='Y',
                secondary_bone_axis='X',
                # 网格设置
                mesh_smooth_type='OFF',
                use_tspace=False,
                # 确保导出骨骼权重
                use_mesh_modifiers=True,
                # Unity兼容性
                axis_forward='-Z',
                axis_up='Y',
            )
            
            self.report({'INFO'}, f"Successfully imported and exported: {output_path}")
            print("NPZ to FBX conversion completed!")
            
            return {'FINISHED'}
            
        except Exception as e:
            self.report({'ERROR'}, f"Error importing NPZ: {str(e)}")
            print(f"Error: {e}")
            import traceback
            traceback.print_exc()
            return {'CANCELLED'}

def menu_func_import(self, context):
    self.layout.operator(NPZToFBXOperator.bl_idname, text="FrankMocap NPZ Animation (.npz)")

def register():
    bpy.utils.register_class(NPZToFBXOperator)
    bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
    print("NPZ to FBX importer registered!")

def unregister():
    bpy.utils.unregister_class(NPZToFBXOperator)
    bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)

def convert_npz_to_fbx(npz_path, fbx_path=None, use_vertex_animation=True):
    """直接转换NPZ文件到FBX（用于命令行调用）"""
    import os
    
    if not os.path.exists(npz_path):
        print(f"Error: NPZ file not found: {npz_path}")
        return False
    
    if fbx_path is None:
        fbx_path = npz_path.replace('.npz', '.fbx')
    
    try:
        print(f"Loading NPZ file: {npz_path}")
        
        # 加载NPZ数据
        data = np.load(npz_path)
        print(f"NPZ keys: {list(data.keys())}")
        
        # 清理场景
        clear_scene()
        
        # 检查和验证动画数据
        print("Validating animation data...")
        validate_animation_data(data)
        
        # 创建网格（如果有顶点数据）
        mesh_obj = None
        if 'vertices' in data and 'faces' in data:
            print("Creating SMPL-X mesh...")
            vertices = data['vertices']
            faces = data['faces']
            mesh_obj = create_smplx_mesh(vertices, faces)
            
            if use_vertex_animation:
                print("Using vertex animation (more accurate)...")
                # 直接使用顶点动画，更准确
                apply_vertex_animation_only(mesh_obj, data['vertices'])
            else:
                print("Using bone animation...")
                # 创建骨架并绑定
                armature_obj = create_smplx_armature()
                
                # 应用骨骼权重绑定
                print("Applying bone weights...")
                try:
                    apply_smplx_weights(mesh_obj, armature_obj)
                    mesh_obj.select_set(True)
                    armature_obj.select_set(True)
                    bpy.context.view_layer.objects.active = armature_obj
                    bpy.ops.object.parent_set(type='ARMATURE')
                    print("✅ Bone weights applied successfully")
                except Exception as e:
                    print(f"Warning: Weight binding error: {e}")
                    apply_automatic_weights(mesh_obj, armature_obj)
                
                # 应用动画数据
                print("Applying animation data...")
                apply_animation_data(armature_obj, data)
        
        # 导出FBX
        print(f"Exporting to FBX: {fbx_path}")
        
        # 选择所有对象
        bpy.ops.object.select_all(action='SELECT')
        
        # 导出FBX（根据动画类型调整设置）
        export_settings = {
            'filepath': fbx_path,
            'check_existing': True,
            'use_selection': True,
            # Unity兼容性
            'axis_forward': '-Z',
            'axis_up': 'Y',
            # 网格设置
            'mesh_smooth_type': 'OFF',
            'use_tspace': False,
        }
        
        if use_vertex_animation:
            # 顶点动画设置
            export_settings.update({
                'bake_anim': True,
                'bake_anim_use_all_bones': False,
                'bake_anim_use_nla_strips': False,
                'bake_anim_use_all_actions': False,
                'bake_anim_force_startend_keying': True,
                # 确保导出形状键
                'use_mesh_modifiers': True,
            })
        else:
            # 骨骼动画设置
            export_settings.update({
                'bake_anim': True,
                'bake_anim_use_all_bones': True,
                'bake_anim_use_nla_strips': False,
                'bake_anim_use_all_actions': False,
                'bake_anim_force_startend_keying': True,
                # 骨骼设置
                'add_leaf_bones': False,
                'primary_bone_axis': 'Y',
                'secondary_bone_axis': 'X',
                # 确保导出骨骼权重
                'use_mesh_modifiers': True,
            })
        
        bpy.ops.export_scene.fbx(**export_settings)
        
        print("NPZ to FBX conversion completed successfully!")
        return True
        
    except Exception as e:
        print(f"Error: {e}")
        import traceback
        traceback.print_exc()
        return False

if __name__ == "__main__":
    import sys
    
    register()
    
    print(f"Command line arguments: {sys.argv}")
    
    # 检查命令行参数
    if len(sys.argv) > 1:
        # Blender使用 -- 分隔符来分离脚本参数
        script_args = []
        try:
            # 找到 -- 分隔符的位置
            separator_index = sys.argv.index('--')
            # 获取 -- 之后的所有参数
            script_args = sys.argv[separator_index + 1:]
        except ValueError:
            # 如果没有找到 --，尝试查找脚本文件名后的参数
            for i, arg in enumerate(sys.argv):
                if arg.endswith('blender_npz_to_fbx.py') and i + 1 < len(sys.argv):
                    script_args = sys.argv[i + 1:]
                    break
        
        if script_args:
            print(f"Script arguments found: {script_args}")
            npz_path = script_args[0]
            fbx_path = script_args[1] if len(script_args) > 1 else None
            success = convert_npz_to_fbx(npz_path, fbx_path)
            if success:
                print("✅ Conversion successful!")
            else:
                print("❌ Conversion failed!")
                sys.exit(1)
        else:
            # 尝试使用默认路径
            default_npz = "./output_npz/smplx_animation.npz"
            if os.path.exists(default_npz):
                print(f"Using default NPZ file: {default_npz}")
                success = convert_npz_to_fbx(default_npz)
                if success:
                    print("✅ Conversion successful!")
                else:
                    print("❌ Conversion failed!")
                    sys.exit(1)
            else:
                print("Usage: blender --background --python blender_npz_to_fbx.py -- <npz_file> [fbx_file]")
                print(f"Default file not found: {default_npz}")
                sys.exit(1)
    else:
        # GUI模式：显示文件选择对话框
        try:
            bpy.ops.import_anim.npz_to_fbx('INVOKE_DEFAULT')
        except:
            print("Could not invoke file dialog in background mode")
            print("Please specify NPZ file path as argument")